package com.game;

import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;

/**
 * @author 小王
 */
public class Tetris extends JFrame {

    //1主方法
    public static void main(String[] args) {
        //创建俄罗斯方块游戏对象
        Tetris tetris = new Tetris();
        //设置菜单栏
        JMenuBar menubar = new JMenuBar();
        //添加菜单栏到tetris
        tetris.setJMenuBar(menubar);
        //创建菜单对象
        JMenu menugame = new JMenu("游戏");
        JMenu menuhelp = new JMenu("帮助");
        //添加菜单到菜单栏
        menubar.add(menugame);
        menubar.add(menuhelp);
        //添加菜单项到菜单
        JMenuItem game = menugame.add("新游戏");
        JMenuItem goon = menugame.add("继续游戏");
        JMenuItem pause = menugame.add("暂停游戏");
        JMenuItem exit = menugame.add("退出游戏");
        JMenuItem gameMessage = menuhelp.add("游戏说明");

        //设置窗体大小
        tetris.setSize(440, 550);       
        //设置窗体居中弹出
        tetris.setLocationRelativeTo(null);
        //设置窗体大小不可变
        tetris.setResizable(false);
        //设置窗体标题
        tetris.setTitle("俄罗斯方块");
        //设置窗体可见
        tetris.setVisible(true);
        //设置关闭窗体
        tetris.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    //2主类成员变量俄罗斯方块初始化坐标
    private static int x = 0;
    private static int y = 0;
    private static int z = 0;
    private static int w = 0;

    //3构造
    public Tetris() {
        TetrisPanel tpanel = new TetrisPanel();
        this.add(tpanel);
        //tpanle对象实现了事件监听
        addKeyListener(tpanel);
    }

    //4窗体内部类集成窗体JPanel实现接口KeyListener键盘监听事件
    public static class TetrisPanel extends JPanel implements KeyListener {
        //定义游戏的围墙数组
        private final int map[][] = new int[13][23];
        //定义方块的7种类形并初始化
        private int blockType = 0;
        //定义每个方块的4种形态数据并初始化
        private int turnState = 0;
        private int count;
        private int ti;

        //声明一个定时器对象
        Timer timer;

        //10计时内部类     没看懂
        private class TimerListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (isCollide(x, y + 1, blockType, turnState)) {
                    //如果碰撞就调用add()方法
                    add(x, y, blockType, turnState);
                    //消除满格的行
                    deletLine();
                    ++count;
                    newBlock();
                    //如果没有发生碰撞就向下移动
                } else {
                    y++;
                }
                ti++;
                repaint();
            }
        }

        /*
         *   画出俄罗斯方块七个图像的4种状态
         */
        private final int[][][] shapes = {
            //长条形
            {{0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}},
            //T字形
            {{0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}},
            //田字形
            {{1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
            //正S形
            {{0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}},
            //反S形
            {{1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}},
            //正L形
            {{1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
            {0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
            {1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
            //反L形
            {{0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0},
            {1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}},};

        //5构造
        public TetrisPanel() {          
            drawWall();
            cleanMap();
            newBlock();
            //每秒执行一次TimerListener
            timer = new Timer(1000, new TimerListener());
            //启动计时内部类
            timer.start();
        }

        //6清游戏屏方法
        private void cleanMap() {
            for (int i = 1; i < 11; i++) {
                for (int j = 1; j < 21; j++) {
                    map[i][j] = 0;
                }
            }
        }

        //7声明围墙
        private void drawWall() {
            //标记底围墙
            for (int i = 0; i < 12; i++) {
                //map[i][0] = 2;
                map[i][21] = 2;
            }
            //标记两侧围墙
            for (int j = 0; j < 22; j++) {
                map[0][j] = 2;
                map[11][j] = 2;
            }
        }

        //8重写JPanel的paintComponent画组件方法
        @Override
        public void paintComponent(Graphics g) {
            //调用panintComponent父类的构造清理痕迹
            super.paintComponent(g);
            //遍历围墙
            for (int j = 0; j < 22; j++) {
                for (int i = 0; i < 12; i++) {
                    //如果被标记为了围墙就画透明方格
                    if (map[i][j] == 2) {
                        //画空心方块
                        g.drawRect(i * 20, j * 20, 20, 20);
                    }
                    if (map[i][j] == 1) {
                        //画实心方块
                        g.fillRect(i * 20, j * 20, 20, 20);
                    }
                }
            }

            /*画出俄罗斯方块一维数组看成二维形状用下面就表示I形方块
             *      1，0，0，0，
             *      1，0，0，0，
             *      1，0，0，0，
             *      1，0，0，0，
             *      下面的循环里 i 代表该一维数组的索引，0-15；
             */
            for (int i = 0; i < 16; i++) {
                if (shapes[blockType][turnState][i] == 1) {
                    g.fillRect(((x + 1 + i % 4) * 20), ((y + i / 4) * 20), 20, 20);
                }
            }

            for (int i = 0; i < 16; i++) {
                if (shapes[blockType][turnState][i] == 1) {
                    g.fillRect(((z + 1 + i % 4) * 15), ((w + i / 4) * 15), 15, 15);
                }
            }

            g.drawLine(260, 60, 530, 60);
            g.drawString("得分：" + score, 280, 80);
            g.drawLine(260, 90, 530, 90);
            g.drawString("时间：" + ti, 280, 120);

        }

        //9生成一个新俄罗斯方块
        private void newBlock() {

            //随机生成7种方块类型的一个
            blockType = (int) (Math.random() * 100) % 7;
            //随机生成该种类型方块的一个形态
            turnState = (int) (Math.random() * 100) % 4;
            z = 20;
            w = 12;
            x = 4;
            y = 0;

            if (gameOver() == true) {
                cleanMap();
                newBlock();
                drawWall();
                score = 0;
                JOptionPane.showMessageDialog(null, "GameOver");
            }
        }

        //判断是否游戏结束的方法
        public boolean gameOver() {
            if (isCollide(x, y, blockType, turnState) == true) {
                return true;
            }
            return false;
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

        //重写KeyListener键盘监听事件的监听键盘方法
        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_SPACE:
                    turn();
                    break;
                case KeyEvent.VK_DOWN:
                    down();
                    break;
                case KeyEvent.VK_LEFT:
                    left();
                    break;
                case KeyEvent.VK_RIGHT:
                    right();
                    break;
            }
        }

        //旋转键盘
        private void turn() {
            int temp = turnState;
            turnState = (turnState + 1) % 4;
            //如果发生碰撞就返回原来状态，否则就执行旋转
            if (isCollide(x, y, blockType, turnState)) {
                turnState = temp;
            } else {
                repaint();
            }
        }

        //向下键
        private void down() {
            if (isCollide(x, y + 1, blockType, turnState)) {
                //如果碰撞就调用add()方法
                add(x, y, blockType, turnState);
                //消除满格的行
                deletLine();
                ++count;
                newBlock();

                //如果没有发生碰撞就向下移动
            } else {
                y++;
            }
            repaint();
        }

        //声明一个分数
        int score = 0;

        //消除满格字的行
        public void deletLine() {
            int c = 0;
            for (int b = 0; b < 21; b++) {
                for (int a = 0; a < 11; a++) {
                    if (map[a][b] == 1) {
                        c++;
                        if (c == 10) {
                            score += 100;
                            for (int d = b; d > 0; d--) {
                                for (int e = 0; e < 11; e++) {
                                    map[e][d] = map[e][d - 1];
                                }
                            }
                        }
                    }
                }
                c = 0;
            }
        }

        //
        public void add(int x, int y, int blockType, int turnState) {
            int i = 0;
            for (int a = 0; a < 4; a++) {
                for (int b = 0; b < 4; b++) {
                    if (map[x + 1 + b][y + a] == 0) {
                        map[x + 1 + b][y + a] = shapes[blockType][turnState][i];
                    }
                    i++;
                }
            }
        }

        //向左键
        private void left() {
            //如果没有发生碰撞就向左移动
            if (!isCollide(x - 1, y, blockType, turnState)) {
                x--;
            }
            repaint();
        }

        //向右键
        private void right() {
            //如果没有发生碰撞就向右移动
            if (!isCollide(x + 1, y, blockType, turnState)) {
                x++;
            }
            repaint();
        }

        //校验俄罗斯方块碰围墙          难点
        public boolean isCollide(int x, int y, int blockType, int turnState) {
            for (int a = 0; a < 4; a++) {
                for (int b = 0; b < 4; b++) {
                    /*
                     *   1,0,0,0
                     *   1,1,0,0
                     *   0,1,0,0
                     *   0,0,0,0
                     */
                    if (((shapes[blockType][turnState][a * 4 + b] == 1) && (map[x + 1 + b][y + a] == 1)) || ((shapes[blockType][turnState][a * 4 + b] == 1) && (map[x + 1 + b][y + a] == 2))) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}

